package com.stxx.stiot2.gateway.example;

import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.stxx.stiot2.domain.SysConfig;
import com.stxx.stiot2.domain.ext.ReportData;
import com.stxx.stiot2.event.dto.T2pMsg;
import com.stxx.stiot2.external.RedisServer;
import com.stxx.stiot2.gateway.AbstractTcpServer;
import com.stxx.stiot2.service.SysConfigService;
import com.stxx.stiot2.utils.ServerUtils;
import com.stxx.stiot2.utils.StrTool;
import lombok.Data;
import lombok.Value;
import org.noear.solon.Solon;
import org.noear.solon.expression.snel.SnEL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.KeyValuePair;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

public class TcpServer extends AbstractTcpServer {
    private static final Logger logger = LoggerFactory.getLogger(AbstractTcpServer.class);
    @Override
    public T2pMsg receive(String topic, String message) {
        //logger.info(message);
        RedisServer redisServer = Solon.context().getBean(RedisServer.class);
        //logger.info("Tcp消息：\n"+message);

        if(message==null||!message.startsWith("#")){
            return null;
        }
        if(message.endsWith("*")){
            logger.info(message);
            return null;
        }
        String gatewayCode = "tcp001";

        Map<String,Object> data = new HashMap<>();
        StringBuilder sb = new StringBuilder();

        String terminalCode = null;
        Date time = null;
        String[] messageList = message.split("\n");
        for(String msgLine : messageList){
            sb.setLength(0);
            sb.append(msgLine);
            StrTool.read(sb,1);
            String bsic = StrTool.read(sb,8);
            //data.put("bsic",body);


            time = DateUtil.parse(StrTool.read(sb,12),"yyMMddHHmmss");
            terminalCode = StrTool.read(sb,10);

            char ch = StrTool.read(sb,1).charAt(0);
            if(ch >= 'A' && ch <= 'Z'){
                data.put("soc",1);
            }else{
                data.put("soc",0);
            }
            String data1 = StrTool.read(sb,2);
            String data2 = StrTool.read(sb,2);
            String data3 = StrTool.read(sb,2);

            if (Character.toUpperCase(ch)=='A'){
                //data.put("addr",Integer.parseInt(data1,16));
            }else if(Character.toUpperCase(ch)=='B'){
                data.put("s_damage",true);
            }else if(Character.toUpperCase(ch)=='S'){
                int i1 =Integer.parseInt(data1);
                if(i1==1){
                    data.put("s_alarm",true);
                }else if(i1==2){
                    data.put("s_accel",true);
                }
            }else if(Character.toUpperCase(ch)=='H'){
                data.put("spo2",Integer.parseInt(data2,16));
                data.put("hr",Integer.parseInt(data1,16));
            }else if(Character.toUpperCase(ch)=='T'){
                data.put("body_tem",Float.parseFloat(data1+"."+data2));
                //data.put("tem",Integer.parseInt(data3));
                //定位
                Integer rssi = Integer.parseInt(data3);

                //data.put("rssi",rssi);
                String key = "position:" + terminalCode;
                PositionCache positionCache = redisServer.getObj(key, PositionCache.class);
                if (positionCache == null) {
                    positionCache = new PositionCache();
                }
                positionCache.put(bsic, rssi);
                redisServer.setObj(key, positionCache);

                Map<String,Double> mm = positionCache.getDistances2();
                String positionStr = JSONUtil.toJsonStr(mm);
                data.put("position", positionStr);
                logger.info(terminalCode+":"+data);

            }
        }

        T2pMsg message2 = new T2pMsg();
        message2.setGatewayCode(gatewayCode);
        message2.setTerminalCode(terminalCode);
        ReportData reportData = new ReportData();
        reportData.setProperties(data);
        reportData.setProfile("bracelet_gw");
        reportData.setTimestamp(time.getTime());
        message2.setData(reportData);
        return message2;
    }

    @Data
    public static class PositionCache implements Serializable {
        private final static Integer queue_length = 8;
        private final static Integer timeout = 10;
        private final static Integer maxSize = 5;
        private Map<String, RssiItem> rssiMap;
        private Map<String,Double> distances;
        public void put(String bsic,Integer rssi){
            if(rssiMap==null)rssiMap=new HashMap<>();
            if(distances==null)distances=new HashMap<>();

            RssiItem rssiItem = null;
            if(rssiMap.containsKey(bsic)){
                rssiItem = rssiMap.get(bsic);
            }else{
                rssiItem = new RssiItem();
                rssiItem.setBsic(bsic);
                rssiItem.setRssiList(new ArrayList<>(queue_length+1));
                rssiMap.put(bsic,rssiItem);
            }
            rssiItem.setTs(System.currentTimeMillis());
            rssiItem.getRssiList().add(rssi);
           // rssiItem.setMin(rssiItem.getRssiList().stream().mapToInt(Integer::intValue).average().getAsDouble());
            rssiItem.setMin(rssiItem.getRssiList().stream().min(Integer::compareTo).get().doubleValue());
            for(int i=0;i<rssiItem.getRssiList().size() - queue_length;i++){
                rssiItem.getRssiList().remove(0);
            }

            Iterator<Map.Entry<String, RssiItem>> iterator = rssiMap.entrySet().iterator();
            List<RssiItem> list = new ArrayList<>();
            while (iterator.hasNext()) {
                Map.Entry<String, RssiItem> entry = iterator.next();
                if (System.currentTimeMillis()>entry.getValue().getTs()+timeout*1000) {
                    iterator.remove();
                }else{
                    list.add(entry.getValue());
                }
            }
            list.sort((item1,item2)->{
                return item2.getMin().compareTo(item1.getMin());
            });
            distances.clear();
            int size = list.size()>3?3:list.size();
            for(int i=0; i<size; i++){
                RssiItem item = list.get(i);
                distances.put(item.getBsic(),item.getMin());
            }
        }

        Map<String,Double> getDistances2(){
            Map<String,Double> distances2=new HashMap<>();
            this.distances.forEach((k,v)->{
                double d = 0.0;
                if(v<=65){
                    d = 0+5*(v-35)/(65-35);
                }else if(v<=75){
                    d =5+5*(v-65)/(75-65);
                }else if(v<=80){
                    d =10+5*(v-75)/(80-75);
                }else if(v<=85){
                    d =15+10*(v-80)/(85-80);
                }else{
                    d =25+40*(v-85)/(100-85);
                }
                distances2.put(k, BigDecimal.valueOf(d).setScale(2, RoundingMode.HALF_DOWN).doubleValue());
            });
            return distances2;
        }
    }

    @Data
    static class RssiItem implements Serializable {
        private Long ts;
        private String bsic;
        private Double min;
        private List<Integer> rssiList;
    }
}
